<?php
namespace app\libs;

/**
 * 文件上传
 *
 * $up = new Uploader();
 * $up->setConfigs([...]);
 * $up->uploadFile('file');
 *
 */
class Uploader {
    public static $UPLOAD_SUCCESS = 0;
    public static $UPLOAD_ERROR_EMPTY = 1;
    public static $UPLOAD_ERROR_TOOBIG = 2;
    public static $UPLOAD_ERROR_NOTALLOWED_TYPE = 3;
    public static $UPLOAD_ERROR_INTERNAL = 10;

    public $configs = [
        // base path to save file eg. /www/data
        'basePath' => '',
        // sub dir to save file eg. mysite/user
        'subPath' => '',
        'rotatePattern' => 'Ym',
        'allowType' => '.jpg, .jpeg, .png',
        // Byte - 1 MB
        'maxSize' => 1048576,
        'randomName' => true,
        // use given name to save file. can not use this feature unless set randomName = false
        'saveName' => '',
        'fileNameLength' => 20
    ];

    // base + sub + rotatePattern
    public $savePath = '';
    public $originalFileName = '';
    /**
     * @property string 以点开头的扩展名
     */
    public $extensionName = '';
    public $uploadingFiles = [];
    public $uploadedFiles = [];

    public function setConfigs($args) {
        foreach($args as $key => $value) {
            $this->configs[$key] = $value;
        }

        $this->configs['basePath'] = rtrim($this->configs['basePath'], '/');
        $this->configs['subPath'] = rtrim($this->configs['subPath'], '/');
    }

    /**
     * 重新排列上传文件
     *
     * @param string $fileField
     */
    private function reSortFile($fileField) {
        // input name 没有 [] 时是字符串
        // 有 [] 时是数组
        $uploadFileArray = $_FILES[$fileField];

        $onlyOne = is_string($uploadFileArray['name']);

        $fileNumber = $onlyOne ? 1 : count($uploadFileArray['name']);
        $fileKeys = ['name', 'type', 'tmp_name', 'error', 'size'];

        for($i=0; $i<$fileNumber; $i++) {
            foreach($fileKeys as $value) {
                $this->uploadingFiles[$i][$value] = $onlyOne
                    ? $uploadFileArray[$value]
                    : $uploadFileArray[$value][$i];
            }
        }
    }

    /**
     * 根据配置创建上传目录 basePath + subPath + rotatePattern
     */
    private function initSavePath() {
        $this->savePath = $this->configs['basePath'];

        if('' !== $this->configs['subPath']) {
            $this->savePath .= '/' . $this->configs['subPath'];
        }

        if('' !== $this->configs['rotatePattern']) {
            $this->savePath .= '/' . date($this->configs['rotatePattern']);
        }

        if( !is_dir($this->savePath) ) {
            $dirs = explode('/', $this->savePath);
            $realDir = '';

            for($i=0, $len=count($dirs); $i<$len; $i++) {
                if('' === $dirs[$i]) {
                    continue;
                }

                if('.' === $dirs[$i]) {
                    $realDir .= './';

                } else {
                    $realDir = $realDir . $dirs[$i] . '/';
                }

                if(!is_dir($realDir)) {
                    mkdir($realDir);
                }
            }
        }
    }

    private function startUpload() {
        $code = Uploader::$UPLOAD_SUCCESS;

        for($i=0, $len=count($this->uploadingFiles); $i<$len; $i++) {
            // check file status
            if(0 !== $this->uploadingFiles[$i]['error']) {
                $code = Uploader::$UPLOAD_ERROR_INTERNAL;

                break;
            }

            $this->originalFileName = $this->uploadingFiles[$i]['name'];
            $this->extensionName = $this->getExtensionName();

            // check type
            if(!$this->isLegalType()) {
                $code = Uploader::$UPLOAD_ERROR_NOTALLOWED_TYPE;

                break;
            }

            // check size
            if(!$this->isLegalSize($i)) {
                $code = Uploader::$UPLOAD_ERROR_TOOBIG;

                break;
            }

            $this->copyFile($i);
        }

        return $code;
    }

    private function getExtensionName() {
        $pos = strrpos($this->originalFileName, '.');

        return substr($this->originalFileName, $pos);
    }

    private function isLegalType() {
        if(false !== strpos($this->configs['allowType'], $this->extensionName)) {
            return true;
        }

        return false;
    }

    private function isLegalSize($i) {
        $fileSize = $this->uploadingFiles[$i]['size'];

        if($fileSize <= $this->configs['maxSize']) {
            return true;
        }

        return false;
    }

    private function copyFile($i) {
        $newFileName = $this->generateFileName();

        @move_uploaded_file($this->uploadingFiles[$i]['tmp_name'],
            $this->savePath . '/' . $newFileName);

        $this->uploadedFiles[] = $newFileName;
    }

    private function generateFileName() {
        $str = 'abcdefghijklmnopqrstuvwxyz_0123456789';
        $chars = $this->originalFileName;

        if($this->configs['randomName']) {
            $chars = '';

            // 14 是文件名的时间戳前缀
            for($i=0; $i<$this->configs['fileNameLength'] - 14; $i++) {
                $chars .= substr($str, mt_rand(0, strlen($str)-1), 1);
            }

            $chars = date('YmdHis', time()) . $chars;
            $chars = $chars . $this->extensionName;

        } else {
            // 给定了使用指定的名字
            if('' !== $this->configs['saveName']) {
                $chars = $this->configs['saveName'] . $this->extensionName;
            }
        }

        return $chars;
    }

    /**
     * 上传文件 入口
     *
     * @param string $fieldName 文件表单名
     */
    public function uploadFile($fieldName) {
        if(0 === count($_FILES)) {
            return Uploader::$UPLOAD_ERROR_EMPTY;
        }

        //重新排列上传文件
        $this->reSortFile($fieldName);

        $this->initSavePath();

        return $this->startUpload();
    }

    /**
     * 获取上传文件
     *
     * @return array
     */
    public function getUploadedFile() {
        $path = str_replace($this->configs['basePath'], '', $this->savePath);

        foreach($this->uploadedFiles as $i => $val) {
            $this->uploadedFiles[$i] = $path . '/' . $val;
        }

        return $this->uploadedFiles;
    }

    public function getFirstUploadedFile() {
        $ret = $this->getUploadedFile();

        if(isset($ret[0])) {
            return $ret[0];
        }

        return '';
    }

    /**
     * 上传一个 base64 图片
     */
    public function saveBase64Image($base64str) {
        $ret = $base64str;
        $mime = '';
        $pos = strpos($ret, ',');

        if(false !== $pos) {
            $mime = substr($ret, 0, $pos);
            $ret = substr($ret, $pos + 1);
        }

        if( false !== strpos($mime, '/jpeg') || false !== strpos($mime, '/jpg') ) {
            $this->extensionName = '.jpg';
        } else if( false !== strpos($mime, '/png') ) {
            $this->extensionName = '.png';
        }

        if('' === $this->extensionName) {
            return '';
        }

        $ret = str_replace(' ', '+', $ret);
        $decode = base64_decode($ret);
        $newFileName = $this->generateFileName();

        $this->initSavePath();
        @file_put_contents($this->savePath . '/' . $newFileName, $decode);
        $this->uploadedFiles[] = $newFileName;
    }
}
